// ========================================================================
// Copyright 2009 NEXCOM Systems
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.cipango.littleims.scscf.registrar;

import java.io.IOException;
import java.math.BigInteger;
import java.security.AlgorithmParameterGenerator;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.crypto.*;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.URI;

import org.apache.log4j.Logger;
import org.cipango.diameter.AVPList;
import org.cipango.diameter.api.DiameterServletAnswer;
import org.cipango.diameter.base.Common;
import org.cipango.diameter.ims.Cx;
import org.cipango.ims.AuthenticationScheme;

import org.cipango.littleims.scscf.cx.CxManager;
import org.cipango.littleims.scscf.registrar.AuthorizationException.Reason;
import org.cipango.littleims.scscf.util.MessageSender;
import org.cipango.littleims.scscf.util.NonceManager;
import org.cipango.littleims.util.AuthorizationHeader;
import org.cipango.littleims.util.Base64;
import org.cipango.littleims.util.Digest;
import org.cipango.littleims.util.Headers;
import org.cipango.littleims.util.HexString;
import org.cipango.littleims.util.URIHelper;

public class ImsAuthenticator implements Authenticator
{

	public static final Logger __log = Logger.getLogger(ImsAuthenticator.class);
	private static final int DEFAULT_AUTH_TIMEOUT = 5000; // 5s

	private Timer _timer;

	private CxManager _cxManager;
	private NonceManager _nonceManager;
	private SipFactory _sipFactory;
	private String _realm;
	private Registrar _registrar;
	private long _authTimeout = DEFAULT_AUTH_TIMEOUT;
	private Map<String, AuthWaitTimerTask> _secContexts = new HashMap<String, AuthWaitTimerTask>();
	
	private MessageSender _messageSender;

	// mapRequest contient la liste des requetes REGISTER en cours de traitement
	private Map<String, SipServletRequest> mapRequest = new HashMap<String, SipServletRequest>();
	// mapDH stocke les parametres utiles pour realiser Diffie Hellman entre le S-CSCF et le HSS
	private Map<String, String> mapDH = new HashMap<String, String>();
	
	public ImsAuthenticator()
	{
		_timer = new Timer();
		_nonceManager = new NonceManager();
	}


	/* (non-Javadoc)
	 * @see org.cipango.littleims.scscf.registrar.Authenticator#authenticate(boolean, javax.servlet.sip.SipServletRequest)
	 */
	public String authenticate(boolean proxy, SipServletRequest request, Registrar registrar) throws IOException
	{
		this._registrar = registrar;
		try
		{
			URI aor = URIHelper.getCanonicalForm(_sipFactory,  request.getTo().getURI());

			// Recuperation de l'entete Authorization de la requete
			String authorization = request.getHeader(Headers.AUTHORIZATION);
			AuthorizationHeader ah = authorization == null ? null : new AuthorizationHeader(authorization);
			String scheme =null;
			if(ah != null)
				scheme = ah.getAlgorithm();

			if ((ah == null || ah.getNonce() == null || ah.getNonce().trim().equals("")) 
					|| AuthenticationScheme.AKA_ONEWAY_DH.getName().equals(scheme)
					|| AuthenticationScheme.AKA_ONEWAY_PKI.getName().equals(scheme))
			{
				//Si le l'algorithme est une version ONEWAY
				if(AuthenticationScheme.AKA_ONEWAY_DH.getName().equals(scheme) ||
						AuthenticationScheme.AKA_ONEWAY_PKI.getName().equals(scheme)){
					//Recuperation de l'identite prive
					String privateUserIdentity = ah.getUsername();
					SipServletRequest req = _sipFactory.createRequest(request,true);
					
					synchronized (mapRequest)
					{
						//Sotcker la requete recue jusqu'a ce que l'authentification soit realisee				
						this.mapRequest.put(privateUserIdentity,request);
					}
					
					//Extraction du rand contenu dans le nonce
					if(AuthenticationScheme.AKA_ONEWAY_DH.getName().equals(scheme)){
						try{
						//Generer les parametres DH
						DHParameterSpec parametresDH = genParams();

						//Generation des cles
						KeyPairGenerator generateurCles = KeyPairGenerator.getInstance("DH");
						generateurCles.initialize(parametresDH);
						KeyPair cles = generateurCles.generateKeyPair();
						
						KeyFactory kfactory = KeyFactory.getInstance("DiffieHellman");
						DHPublicKeySpec kspec = (DHPublicKeySpec) kfactory.getKeySpec(cles.getPublic(),
						        DHPublicKeySpec.class);
						
						String dhParam = parametresDH.getP()+","+parametresDH.getG()+","+kspec.getY();
						
						//Ajouter les parametres DH au message
						String nonce = ah.getNonce();
						String[] paramNonce = nonce.split(",");
						String newNonce = paramNonce[0]+","+dhParam; //paramNonce[0] = RANDi 
						ah.setParameter(AuthorizationHeader.NONCE_PARAM, newNonce);
						req.setHeader(Headers.AUTHORIZATION,ah.toString());

						DHPrivateKeySpec kspecPrivate = (DHPrivateKeySpec) kfactory.getKeySpec(cles.getPrivate(),
						        DHPrivateKeySpec.class);
						String dhParamPrivate = parametresDH.getP()+","+parametresDH.getG()+","+kspecPrivate.getX();
						
						//Stocker les parametres dans mapDH
						synchronized(mapDH){
							mapDH.put(ah.getUsername(),dhParamPrivate);
						}
						
						//Emettre la requete MAR
						_cxManager.sendMAR(aor, ah, req);
						
						return null;
						}
						catch(Exception e){
							e.printStackTrace();
							_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
						}
					}
				}

				// Emission du message MAR vers le HSS
				_cxManager.sendMAR(aor, ah, request);

				return null;
			}
			// Utilise pour les versions non ONEWAY 
			else
			{
				try
				{
					// probably second REGISTER unless phone uses a stale nonce
					// parse Authorization header

					String privateUserIdentity = null;

					__log.debug("Authorizing registration ...");
					checkRegistration(aor.toString(), ah, request.getMethod());
					privateUserIdentity = ah.getUsername();

					// digest authentication is successful
					__log.debug("Registration of public identity " + aor + " has been authorized");

					return privateUserIdentity;
				}
				catch (AuthorizationException e)
				{
					switch (e.getReason())
					{
					case INVALID:
						__log.debug("Registration is not valid. Sending 400 response");
						_messageSender.sendResponse(request, SipServletResponse.SC_BAD_REQUEST);
						break;
					case ERROR:
						__log.debug("Credentials are not accepted. Sending 403 response");
						_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
						break;
					case STALE:
						__log.debug("Registration is stale. Sending 401 response");
						request.setAttribute("stale", true);
						_cxManager.sendMAR(aor, ah, request);
						break;
					case RESYNCHRONIZATION:
					case INITIAL:
						__log.debug("Registration is initial. Sending 401 response");
						_cxManager.sendMAR(aor, ah, request);
						break;
					default:
						__log.warn("Unknown reason " + e.getReason());
						break;
					}
					return null;
				}
			}
		}
		catch (IOException e)
		{
			__log.warn("Failed to send MAR request", e);
			_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);

			return null;

		}
	}
	
	private DHParameterSpec genParams(){
		try{
			//Initialisation du generateur de parametres
			AlgorithmParameterGenerator generateur = AlgorithmParameterGenerator.getInstance("DH");
			generateur.init(512);

			//Generation
			AlgorithmParameters parametres = generateur.generateParameters();
			DHParameterSpec dhSpec = (DHParameterSpec)parametres.getParameterSpec(DHParameterSpec.class);

			return dhSpec;
		}catch(Exception e){
			System.err.println("Exception dans genParams(): "+e);
		}
		return null;
	}

	public CxManager getCxManager()
	{
		return _cxManager;
	}

	public void setCxManager(CxManager cxManager)
	{
		_cxManager = cxManager;
	}

	/* Handle MAA message
	 *
	 */
	public void handleMaa(DiameterServletAnswer maa)
	{	
		SipServletRequest request = (SipServletRequest) maa.getRequest().getAttribute(SipServletRequest.class.getName());
		String authorization = request.getHeader(Headers.AUTHORIZATION);
		AuthorizationHeader ah = authorization == null ? null : new AuthorizationHeader(authorization);
		
		try
		{
			if (!maa.getResultCode().isSuccess())
			{
				__log.debug("Diameter MAA answer is not valid: " + maa.getResultCode() + ". Sending 403 response");

				_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
				return;
			}
			
			String aor = maa.getRequest().get(Cx.PUBLIC_IDENTITY);

			AVPList sadi =  maa.get(Cx.SIP_AUTH_DATA_ITEM);

			String scheme = sadi.getValue(Cx.SIP_AUTHENTICATION_SCHEME);
			// Si l'alogrithme est DIGEST
			if (AuthenticationScheme.SIP_DIGEST.getName().equals(scheme))
			{
				AVPList digestAuthenticate = sadi.getValue(Cx.SIP_DIGEST_AUTHENTICATE);
				String realm = digestAuthenticate.getValue(Common.DIGEST_REALM) == null ? _realm
						: digestAuthenticate.getValue(Common.DIGEST_REALM);
				String algorithm = digestAuthenticate.getValue(Common.DIGEST_ALGORITHM) == null ? AuthenticationScheme.SIP_DIGEST
						.getAlgorithm() : digestAuthenticate.getValue(Common.DIGEST_ALGORITHM);

						// Start Reg-await-auth timer
						AuthWaitTimerTask authTimer = new AuthWaitTimerTask(aor, digestAuthenticate.getValue(Common.DIGEST_HA1));
						_timer.schedule(authTimer, _authTimeout);

						synchronized (_secContexts)
						{
							_secContexts.put(aor, authTimer);
						}
						String nonce = _nonceManager.newNonce();
						__log.debug("Starting reg-await-auth timer for URI: " + aor + ". Nonce: "
								+ nonce);
						send401(request, realm, nonce, request.getAttribute("stale") != null, algorithm);

			}
			// Si l'algorithme est AKAv1
			else if (AuthenticationScheme.DIGEST_AKA_MD5.getName().equals(scheme))
			{
				// TODO ik ck
				byte[] bNonce = sadi.getValue(Cx.SIP_AUTHENTICATE);
				String nonce = Base64.encode(bNonce);
				byte[] xres = sadi.getValue(Cx.SIP_AUTHORIZATION);
				AuthWaitTimerTask authTimer = new AuthWaitTimerTask(aor, xres);
				_timer.schedule(authTimer, _authTimeout);

				synchronized (_secContexts)
				{
					_secContexts.put(aor, authTimer);
				}
				send401(request, _realm, nonce, request.getAttribute("stale") != null, AuthenticationScheme.DIGEST_AKA_MD5.getAlgorithm());
			}
			// Si l'alogrithme est un ONEWAY
			else if(AuthenticationScheme.AKA_ONEWAY_DH.getName().equals(scheme) ||
					AuthenticationScheme.AKA_ONEWAY_PKI.getName().equals(scheme))
			{
				SipServletRequest sip = this.mapRequest.get(ah.getUsername());

				//Si une requete sip a ete enregistre par cet utilisateurs
				if(sip != null){

					String authRequest = sip.getHeader(Headers.AUTHORIZATION);
					AuthorizationHeader ahRequest = authorization == null ? null : new AuthorizationHeader(authRequest);
					String privateUserIdentity = ahRequest.getUsername();
					try{
						// Recuperation informations de l'entete pour calcul du hash
						String cseq = sip.getHeader(Headers.CSEQ);
						String contentLength = sip.getHeader(Headers.CONTENT_LENGTH);
						String from = sip.getHeader(Headers.FROM);
						String contact = sip.getHeader(Headers.CONTACT);
						String expires = sip.getHeader(Headers.EXPIRES);
						String to = sip.getHeader(Headers.TO );
						String verifIntegrity = "CSeq: "+cseq+
						"Content-Length: " + contentLength +
						"From: "+ from +
						"Contact: "+ contact +
						"Expires: " + expires +
						"To: "+to;
						String integrityRec = ahRequest.getParameter("hash");

						// Recuperation des cles, du xres et de l'autn concatene avec la cle Publique du HSS
						// xres,ck,ik et autn sont chiffres avec la cle k de DH
						byte[] encXresByte = sadi.getValue(Cx.SIP_AUTHORIZATION);
						byte[] encCK = sadi.getValue(Cx.CONFIDENTIALITY_KEY);
						byte[] encIK = sadi.getValue(Cx.INTEGRITY_KEY);
						byte[] autnAndKey = sadi.getValue(Cx.SIP_AUTHENTICATE);
						
						// Separation de l'autn et de la cle publique
						String autnAndKeyStr = new String(autnAndKey);
						String[] split = autnAndKeyStr.split(",");
						String pubClient = split[1];
						byte[] encAutn = HexString.hexToBuffer(split[0]);
						
						// Extraction des parametres necessaires a Diffie Hellman
						String dhParam = mapDH.get(ah.getUsername());
						String[] splitParam = dhParam.split(",");
						String dhParamClient = ah.getResponse();
						
						BigInteger P = new BigInteger(splitParam[0]);
						BigInteger G = new BigInteger(splitParam[1]);
						BigInteger X = new BigInteger(splitParam[2]);
						BigInteger Y = new BigInteger(pubClient);
						
						// Generation des cles Publique et Privee pour DH
						DHPublicKeySpec beta = new DHPublicKeySpec(Y, P, G);
						DHPrivateKeySpec alpha = new DHPrivateKeySpec(X, P, G);
						KeyFactory kfactory = KeyFactory.getInstance("DiffieHellman");
						PublicKey pub = kfactory.generatePublic(beta);
						PrivateKey priv = kfactory.generatePrivate(alpha);
						
						// Generation de la cle k de DH
						KeyAgreement ka= KeyAgreement.getInstance("DH");
					    ka.init(priv);
					    ka.doPhase(pub, true);
					    SecretKey k = ka.generateSecret("DES");
					    
					    // Dechiffrement des parametres avec la cle k de DH
					    byte[] xresByte = decrypt(k,encXresByte);
					    byte[] CK = decrypt(k,encCK);
					    byte[] IK = decrypt(k,encIK);
					    byte[] autn = encrypt(CK,decrypt(k,encAutn));
					   
						// Calcul du hash de l'entete
						MessageDigest sha1 = MessageDigest.getInstance("SHA1");
						byte[] byteHash = sha1.digest(verifIntegrity.getBytes());
						byte[] encryptedHash = encrypt(IK,byteHash);
						String integrityCalc = HexString.bufferToHex(encryptedHash);
						
						// Si les hashs sont egaux alors l'integrite du message est verifiee
						if(integrityCalc.equals(integrityRec)){  
							String res = HexString.bufferToHex(decrypt(CK,HexString.hexToBuffer(ahRequest.getResponse())));
							String xres = HexString.bufferToHex(xresByte);

							// Verification de RESi et XRES
							if(xres.equals(res)){					
								// Envoyer le SAR en executant la commande doRegister sur le registrar
								this._registrar.doRegister(sip, privateUserIdentity,autn,CK,IK);
							}
							// Si different
							else{
								// SEND FORBIDDEN
								_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
							}
						}
						else{
							// SEND FORBIDDEN
							_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
						}
						synchronized (mapRequest)
						{
							// Supprimer la requete enregistree
							this.mapRequest.remove(privateUserIdentity);
						}
						synchronized(mapDH){
							mapDH.remove(ah.getUsername());
						}

					}
					catch (Exception e){
						e.printStackTrace();
						_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
					}
				}
			}
			else if(AuthenticationScheme.AKA_ONEWAY_PKI.getName().equals(scheme))
			{
				//CODE pour l'authentification en ONE WAY avec PKI
			}
			else
			{
				__log.warn("Received unsupported scheme: " + scheme + " in MAA response");
				_messageSender.sendResponse(request, SipServletResponse.SC_FORBIDDEN);
			}
		}
		finally
		{
			// The SipApplicationSession can be invalid if this thread is interrupted before
			// sending the response.
			if (request.getApplicationSession().isValid())
				request.getApplicationSession().invalidate();
		}	
	}

	private byte[] decrypt(byte[] CK, byte[] value){
		Cipher c = null;
		String res = null;
		try {
			c = Cipher.getInstance("AES");

			SecretKeySpec k = new SecretKeySpec(CK, "AES");
			c.init(Cipher.DECRYPT_MODE, k);

			//domaine
			
			return c.doFinal(value);
		} catch (NoSuchAlgorithmException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (NoSuchPaddingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}
	
	private byte[] decrypt(SecretKey k, byte[] value){
		Cipher c = null;

		try {
			c = Cipher.getInstance("DES");
			c.init(Cipher.DECRYPT_MODE, k);
			//domaine	
			return c.doFinal(value);
		} catch (NoSuchAlgorithmException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (NoSuchPaddingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	private byte[] encrypt(byte[] CK, byte[] value){
		Cipher c = null;
		String valueCrypt = null;
		try {
			c = Cipher.getInstance("AES");

			SecretKeySpec k = new SecretKeySpec(CK, "AES");
			c.init(Cipher.ENCRYPT_MODE, k);

			return c.doFinal(value);
		} catch (NoSuchAlgorithmException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (NoSuchPaddingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	private void authWaitExpired(String uri)
	{
		__log.debug("reg-await-auth timer has expired for: " + uri);
		synchronized (_secContexts)
		{
			_secContexts.remove(uri);
		}
	}

	public void checkRegistration(String uri, AuthorizationHeader ah, String method)
	throws AuthorizationException
	{

		// [TS24.229] 1. Check Call-ID
		// RFC 3261 does not mandate it. As a result, we don't check
		// since SIP phones are not IMS-compliant

		// [TS24.229] 2. Stop reg timer
		AuthWaitTimerTask authWaitTimerTask = null;
		synchronized (_secContexts)
		{
			authWaitTimerTask = _secContexts.get(uri.toString());
			if (authWaitTimerTask != null)
			{
				__log.debug("Found security context");
				authWaitTimerTask.cancel();
				_secContexts.remove(uri.toString());
			}
		}

		// [TS24.229] 3. Check if Authorization exists
		// Already done since use this header to start "protected REGISTER"
		// procedure, no integrity-protected header yet

		if (authWaitTimerTask == null)
		{
			__log.info("No security context found for user " + uri.toString());
			throw new AuthorizationException(Reason.STALE);
		}

		// [TS24.229] 4. Check the received authentication response
		String xres;
		try
		{
			switch (authWaitTimerTask.getAuthenticationScheme().getOrdinal())
			{
			case AuthenticationScheme.SIP_DIGEST_ORDINAL:
				xres = Digest.calculateResponseWithHa1(ah, method, authWaitTimerTask.getHa1());
				break;
			case AuthenticationScheme.DIGEST_AKA_MD5_ORDINAL:
				String auts = ah.getAuts();
				if (auts != null)
				{
					throw new AuthorizationException(Reason.RESYNCHRONIZATION);
				}
				xres = Digest.calculateResponse(ah, method, authWaitTimerTask.getXres());
				break;
			default:
				__log.warn("Authentication scheme: "
						+ authWaitTimerTask.getAuthenticationScheme().getName()
						+ " is not supported");
				throw new AuthorizationException(Reason.INVALID);
			}
		}
		catch (IllegalArgumentException _)
		{
			throw new AuthorizationException(Reason.INVALID);
		}

		String res = ah.getResponse();
		if (!res.equals(xres))
		{
			__log.info("Response is not correct for " + uri);
			throw new AuthorizationException(Reason.ERROR);
		}
	}

	private void send401(SipServletRequest request, String realm, String nonce, boolean stale,
			String algorithm)
	{
		try
		{
			__log.debug("Sending 401 response");
			String wwwAuthenticate = "Digest realm=\"" + realm + "\", qop=\"auth\", nonce=\"" + nonce
			+ "\", algorithm=\"" + algorithm + "\"";
			if (stale)
			{
				wwwAuthenticate += ", stale=TRUE";
			}
			_messageSender.sendResponse(request, SipServletResponse.SC_UNAUTHORIZED, Headers.WWW_AUTHENTICATE, wwwAuthenticate);
		}
		catch (Exception e)
		{
			__log.warn("Failed to send 401", e);
		}
	}

	public SipFactory getSipFactory()
	{
		return _sipFactory;
	}

	public void setSipFactory(SipFactory sipFactory)
	{
		_sipFactory = sipFactory;
	}

	public String getRealm()
	{
		return _realm;
	}

	public void setRealm(String realm)
	{
		_realm = realm;
	}

	class AuthWaitTimerTask extends TimerTask
	{
		private String _uri;
		private AuthenticationScheme _authenticationScheme;
		private byte[] _xres;
		private String _ha1;

		public AuthenticationScheme getAuthenticationScheme()
		{
			return _authenticationScheme;
		}

		public byte[] getXres()
		{
			return _xres;
		}

		public String getHa1()
		{
			return _ha1;
		}

		public AuthWaitTimerTask(String uri, byte[] xres)
		{
			_uri = uri;
			_authenticationScheme = AuthenticationScheme.DIGEST_AKA_MD5;
			_xres = xres;
		}

		public AuthWaitTimerTask(String uri, String ha1)
		{
			_uri = uri;
			_authenticationScheme = AuthenticationScheme.SIP_DIGEST;
			_ha1 = ha1;
		}

		public void run()
		{
			authWaitExpired(_uri);
		}

	}

	public MessageSender getMessageSender()
	{
		return _messageSender;
	}


	public void setMessageSender(MessageSender messageSender)
	{
		_messageSender = messageSender;
	}


	public long getAuthTimer()
	{
		return _authTimeout;
	}


	public void setAuthTimer(long authTimer)
	{
		_authTimeout = authTimer;
	}


}
